﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace RobotMapper
{
    internal static class RobotMapperHelper
    {
        /// <summary>
        /// 将源的属性值赋值给目标的属性
        /// </summary>
        public static void SetTargetValue<T, M>(T source, M target, PropertyInfo sourceProperty, PropertyInfo destinationProperty)
        {
            //获取源值
            var value = sourceProperty.GetValue(source, null);
            //为目标属性赋值
            destinationProperty.SetValue(target, value, null);
        }

        public static bool NameMatches(string memberName, string nameToMatch)
        {
            return String.Compare(memberName, nameToMatch, StringComparison.OrdinalIgnoreCase) == 0;
        }

        /// <summary>
        /// 根据上级引用找到本体类型形成循环引用的属性
        /// </summary>
        /// <param name="superior">上级引用</param>
        /// <param name="type">本体类型</param>
        /// <returns></returns>
        public static List<RefType> GetCircalRefType(List<Type> superiors, Type type)
        {
            var allRefType = GetAllRefType(type);
            return allRefType.Where(a => superiors.Contains(a.RefrenceType)).ToList();
        }

        /// <summary>
        /// 找出所有自引用属性
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static List<RefType> GetSelfRefType(Type type)
        {
            return GetAllRefType(type).Where(a => a.RefrenceType == type).ToList();
        }

        /// <summary>
        /// 找出某引用的所有引用+属性
        /// </summary>
        public static List<RefType> GetAllRefType(Type type)
        {
            PropertyInfo[] sourceProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            var genericPros = sourceProperties
                .Where(a => a.PropertyType.IsGenericType && !Filter(a.PropertyType.GetGenericArguments()[0]))
                .Select(a => new RefType() { PropertyName = a.Name, RefrenceType = a.PropertyType.GetGenericArguments()[0] });
            var notgenercPros = sourceProperties
                .Where(a => !a.PropertyType.IsGenericType && !Filter(a.PropertyType))
                .Select(a => new RefType() { PropertyName = a.Name, RefrenceType = a.PropertyType });
            var allRefPros = genericPros.Union(notgenercPros);
            return allRefPros.ToList();
        }

        public static bool Filter(Type type)
        {
            return type.FullName.StartsWith("System.");
        }

        public static string GetPropertyName<T>(Expression<Func<T, object>> exp)
        {
            var rtn = "";
            if (exp.Body is UnaryExpression)
            {
                rtn = ((MemberExpression)((UnaryExpression)exp.Body).Operand).Member.Name;
            }
            else if (exp.Body is MemberExpression)
            {
                rtn = ((MemberExpression)exp.Body).Member.Name;
            }
            else if (exp.Body is ParameterExpression)
            {
                rtn = ((ParameterExpression)exp.Body).Type.Name;
            }
            if (!rtn.Contains(".")) return rtn;
            return rtn.Split('.')[1];
        }

        public static object CreatObject(Type objectType)
        {
            ConstructorInfo defaultCtor = objectType.GetConstructor(new Type[] { });
            DynamicMethod dynMethod = new DynamicMethod(
                name: string.Format("_{0:N}", Guid.NewGuid()),
                returnType: objectType,
                parameterTypes: null);
            var gen = dynMethod.GetILGenerator();
            gen.Emit(OpCodes.Newobj, defaultCtor);
            gen.Emit(OpCodes.Ret);
            var objCreator = dynMethod.CreateDelegate(typeof(Func<object>)) as Func<object>;
            return objCreator();
        }

        public static T CreatObject<T>()
            where T : class
        {
            return (CreatObject(typeof(T)) as T);
        }

        /// <summary>
        /// 所有上级
        /// </summary>
        /// <param name="maps">所有关系</param>
        /// <param name="map">上级</param>
        /// <param name="resultMaps"></param>
        public static void GetAllParentMaps(List<RelationMap> maps, RelationMap map, List<RelationMap> resultMaps)
        {
            if (!map.SupId.HasValue) return;
            var parentMap = maps.SingleOrDefault(a => a.Id == map.SupId);
            if (parentMap == null) return;
            resultMaps.Add(parentMap);
            GetAllParentMaps(maps, parentMap, resultMaps);
        }

        /// <summary>
        /// 获取所有上级Map
        /// </summary>
        /// <param name="maps">当前所有Map</param>
        /// <param name="map">当前Map</param>
        /// <param name="results">结果集</param>
        /// <returns></returns>
        public static List<RelationMap> GetMapParentMaps(List<RelationMap> maps, RelationMap map, List<RelationMap> results = null)
        {
            //表示第一次层
            if (results == null)
            {
                results = new List<RelationMap>();
                results.Add(map);
            }
            if (!map.SupId.HasValue) return results;
            var mapP = maps.SingleOrDefault(a => a.Id == map.SupId);
            if (mapP != null) results.Add(mapP);
            GetMapParentMaps(maps, mapP, results);
            return results;
        }

        public static void GetRelationMap(Type type, RelationMap map, List<RelationMap> maps)
        {
            //找出maps中所有的父级Maps
            var parentMaps = new List<RelationMap>();
            GetAllParentMaps(maps, map, parentMaps);
            //需加上上级
            parentMaps.Add(map);
            //获取所有属性
            PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var item in properties)
            {
                Type propertyType = item.PropertyType;
                if (propertyType.IsGenericType)
                {
                    var genericType = propertyType.GetGenericArguments()[0];
                    //系统对象
                    if (Filter(genericType)) continue;
                    if (parentMaps.Count(a => a.TheType == genericType) > 0) continue;
                    var newMap = new RelationMap()
                    {
                        Id = Guid.NewGuid(),
                        TheType = genericType,
                        PropertyName = item.Name,
                        SupId = map.Id,
                    };
                    maps.Add(newMap);
                    GetRelationMap(genericType, newMap, maps);
                    continue;
                }
                if (Filter(propertyType)) continue;
                if (parentMaps.Count(a => a.TheType == propertyType) > 0) continue;
                var newPMap = new RelationMap()
                {
                    Id = Guid.NewGuid(),
                    TheType = propertyType,
                    PropertyName = item.Name,
                    SupId = map.Id,
                };
                maps.Add(newPMap);
                GetRelationMap(propertyType, newPMap,maps);
            }
        }
    }
}
